home *** CD-ROM | disk | FTP | other *** search
- /*
- * a friend.c
- *
- * by Alex Kourakos
- * Copyright © 1993 by Sokaruok/Xela
- *
- * Written in THINK C v4.0 (I’m still waiting for 6.0)
- *
- * r930623
- * v1.3.0b0
- * Does a few checks to make sure it can run on this computer. Also,
- * recompiled with 68000 code only. Also, if it can’t install itself,
- * it beeps and doesn’t take up system heap space. Version is now Beta.
- * v1.2.0d0
- * Rewrote to get preferences from resource fork and not hard-wired
- * into program.
- * v1.1.2d1
- * Removed references to “mistakes.” I don’t think it’s worth the
- * trouble to make our friend a bad typist.
- * v1.1.2d0
- * Fixed a bug that would only allow exactly 8 strings in the resource
- * fork.
- * v1.1.1d0
- * Fixed a big bug in the way times were selected.
- * v1.1.0d0
- * Wrote my own random number routine, because the QuickDraw one stinks
- * from an INIT.
- * v1.0.1d0
- * Added sounds with each keypress.
- * v1.0.0d0
- * Rewrote to get strings from resources. Made a lot of other changes.
- * v0.1.0d0
- * Now randomly adjusts time between keystrokes and between cycles. Added
- * second message.
- * v0.0.0d0
- * This program wants to be your friend.
- */
-
- #include <MacTypes.h>
- #include <QuickDraw.h>
- #include <OSUtil.h>
- #include <EventMgr.h>
- #include <ResourceMgr.h>
- #include <SoundMgr.h>
- #include <pascal.h>
- #include <SetUpA4.h>
-
-
- /*
- * Program constants.
- */
-
- #define kBaseResID 128
- #define kAsynchronousSound false
- #define _SystemTask 0xa9b4
-
- #define kPrefsType 'PREF'
-
- #define kMaxStr 16
-
- #define kSpaceKeyCode 0x20
- #define kReturnKeyCode 0x0d
-
- /*
- * Resources.
- */
-
- #define rTheStrings kBaseResID
- #define rAnyKeySound kBaseResID
- #define rSpaceKeySound (kBaseResID+1)
- #define rReturnKeySound (kBaseResID+2)
- #define rPrefs kBaseResID
-
-
- /*
- * My types.
- */
-
- typedef struct {
- unsigned long initialDelay,maxTime,maxTypingTime;
- } **FriendPrefsHandle;
-
- typedef void (*SystemTaskPtr)(void);
-
-
- /*
- * Function prototypes.
- */
-
- void main(void);
- void MySystemTask(void);
- int RandomInt(int);
- void TypeChar(char);
-
-
- /*
- * Global variables.
- */
-
- SystemTaskPtr gOldSystemTask;
- unsigned long gLastTickCount,gRandomSeed,gTime;
- Str255 gStrings[kMaxStr];
- int gWhichString,gCharPos;
- short gNumStr;
- Handle gAnyKeySound,gSpaceKeySound,gReturnKeySound;
- FriendPrefsHandle gPrefs;
-
-
- /*
- * Main. This is called first in the INIT code, thanks to THINK’s glue, and
- * it installs everything nicely.
- */
-
- void main(void) {
- Ptr whereWeLive;
- int strCounter;
- SysEnvRec theMac;
-
- /*
- * THINK C is kind enough to stuff our address into A0.
- */
-
- RememberA0();
-
- /*
- * THINK C is also nice enough to let us access our globals from
- * A4, since A5 is probably “in use.”
- */
-
- SetUpA4();
-
- /*
- * Stuff the address (in register A0) into whereWeLive.
- */
-
- asm { move.l A0,whereWeLive }
-
- /*
- * Before we do anything, let’s make sure we’re running on a decent
- * Macintosh. I don’t know any other way to check for the Sound Manager
- * at this point.
- */
-
- SysEnvirons(curSysEnvVers,&theMac);
-
- /*
- * I want at least System 6.
- */
-
- if( theMac.systemVersion < 0x0600 ) {
- SysBeep(666);
- goto done;
- }
-
- /*
- * Get the preferences. They are marked as non-purgeable system heap resources,
- * but the Mac will forget about them if we don’t detach them from this file,
- * which will be closed as soon as the INIT finished loading.
- */
-
- gPrefs = (FriendPrefsHandle)GetResource(kPrefsType,rPrefs);
-
- if( gPrefs == nil ) {
- SysBeep(666);
- goto done;
- }
-
- DetachResource(gPrefs);
-
- /*
- * Initialize the global variables.
- */
-
- gLastTickCount = TickCount();
- gTime = ((*gPrefs)->initialDelay);
- gWhichString = 0;
- gCharPos = 1;
- gRandomSeed = TickCount() + (unsigned long)whereWeLive;
- gNumStr = **((short **)GetResource('STR#',rTheStrings));
- if( gNumStr > kMaxStr )
- gNumStr = kMaxStr;
-
- /*
- * Let’s snag all the strings out of our resource fork.
- */
-
- for( strCounter = 0 ; strCounter < gNumStr ; ++strCounter )
- GetIndString(gStrings[strCounter],rTheStrings,strCounter + 1);
-
- /*
- * Let’s get all the sounds. They too are marked as nonpurgeable
- * system resources and will be detached.
- */
-
- gAnyKeySound = GetResource(soundListRsrc,rAnyKeySound);
- gSpaceKeySound = GetResource(soundListRsrc,rSpaceKeySound);
- gReturnKeySound = GetResource(soundListRsrc,rReturnKeySound);
-
- if( (gAnyKeySound == nil) ||
- (gSpaceKeySound == nil) ||
- (gReturnKeySound == nil) ) {
- SysBeep(666);
- goto done;
- }
-
- DetachResource(gAnyKeySound);
- DetachResource(gSpaceKeySound);
- DetachResource(gReturnKeySound);
-
- /*
- * If we got this far, everything’s okay. Let’s detach ourselves in the
- * system heap.
- */
-
- DetachResource(RecoverHandle(whereWeLive));
-
- /*
- * Save the original SystemTask address, and then replace it with our own
- * (evil laugh.) Note the StripAddress function to ensure this is 32-bit
- * clean.
- */
-
- gOldSystemTask = (SystemTaskPtr)StripAddress(NGetTrapAddress(_SystemTask,ToolTrap));
- NSetTrapAddress((long)StripAddress(MySystemTask),_SystemTask,ToolTrap);
-
- /*
- * Make A4 whatever it was again.
- */
-
- done:
- RestoreA4();
- }
-
-
- /*
- * This is the function that gets called instead of SystemTask. Notice that it
- * is a head patch.
- */
-
- void MySystemTask(void) {
- SystemTaskPtr OldSystemTask;
-
- /*
- * We need to get to our globals.
- */
-
- SetUpA4();
-
- /*
- * Save the address in a local variable, so that it can be called from
- * within the normal stack frame.
- */
-
- OldSystemTask = gOldSystemTask;
-
- /*
- * If we have exceeded our time limit, then do stuff, otherwise, don’t
- * do anything.
- */
-
- if( TickCount() >= (gLastTickCount + gTime) ) {
-
- /*
- * Shove the next character in to the event queue.
- */
-
- TypeChar(gStrings[gWhichString][++gCharPos]);
-
- /*
- * Check to see if we have just typed the last character of the
- * string (the first character in a Pascal string is the length.)
- */
-
- if( gCharPos == gStrings[gWhichString][0] ) {
-
- /*
- * If so, choose a new string, and get a random time, and reset
- * the character position to the first character.
- */
-
- gWhichString = RandomInt(gNumStr);
- gTime = ((*gPrefs)->maxTime) - RandomInt((int)((*gPrefs)->maxTime) >> 2);
- gCharPos = 0;
- }
- else {
-
- /*
- * Otherwise, choose a short time for the next keystroke. If the next
- * character is the same as this one, choose a shorter time (to simulate
- * faster typing of one key over and over). This looks like a mess, but
- * it works.
- */
-
- gTime = (gStrings[gWhichString][gCharPos] == gStrings[gWhichString][gCharPos + 1]) ?
- (long)RandomInt((int)((*gPrefs)->maxTypingTime) >> 1) :
- (((*gPrefs)->maxTypingTime) - (long)RandomInt((int)((*gPrefs)->maxTypingTime)));
- }
-
- /*
- * If something happened, let’s update the last time.
- */
-
- gLastTickCount = TickCount();
- }
-
- /*
- * Restore normal global pointers.
- */
-
- RestoreA4();
-
- /*
- * Call the old SystemTask.
- */
-
- OldSystemTask();
- }
-
-
- /*
- * Returns a random number between 0 and max - 1.
- */
-
- int RandomInt(int max) {
- int returnMe;
- unsigned long a,b;
-
- /*
- * This is from _C_Mathematical_Function_Handbook_ by Baker.
- */
-
- a = gRandomSeed / 127773L;
- b = gRandomSeed % 127773L;
- gRandomSeed = (16807 * b) - (2836 * a);
-
- returnMe = (int)(gRandomSeed) % max;
-
- /*
- * If it is negative, we want it positive.
- */
-
- if( returnMe < 0 )
- return -returnMe;
- return returnMe;
- }
-
-
- /*
- * Places the character into the Event Queue, and then plays the
- * appropriate sound.
- */
-
- void TypeChar(char c) {
- Handle theSound;
-
- switch( c ) {
- case kSpaceKeyCode:
- theSound = gSpaceKeySound;
- break;
-
- case kReturnKeyCode:
- theSound = gReturnKeySound;
- break;
-
- default:
- theSound = gAnyKeySound;
- break;
- }
-
- /*
- * Places an event of type keyDown, with a message of this character. All the
- * other fields of the event record are set appropriately by the Event Manager.
- */
-
- PostEvent(keyDown,(long)(c));
-
- /*
- * Use the Sound Manager to play the sound.
- */
-
- SndPlay(nil,theSound,kAsynchronousSound);
- }
-